Agenda
Announcements
- MDSR Ch 10 programming notebook assigned
MDSR Ch 10 Errata / Tips
- Some sections don’t require programming, but please still include the headers for navigation purposes
- p. 234-235: Rename the result for 20,000 simulations as
sim_results20k
- the authors reuse the object name
sim_results
for the example with 20,000 simulations, but this overwrites the object expected for the plot at the top of p. 235.
- by renaming the object resulting form 20,000 simulations, the ggplot call will still be able to access the intended object
Purposes for Simulation
- Q: What comes to mind when you think of “simulation”
- Q: What are the purposes for simulation?
Intro to Simulation
- Simulations are often useful to learn about the nature of a random process
- We build a realistic “model” of the system/process that we want to study
- The act of building itself often leads to deeper understanding
- We study outcomes produced by our model to build intuition about the nature of the system and it’s outcomes
- Winnowing out hypotheses:
- we often have many competing hypotheses about how a real system works
- we can simulate/generate data from each hypothesis and rule out the ones that aren’t consistent with the observed data
- e.g., our randomization test
- Conditional inference:
- build a system that reflects how a real system works
- use the data that it produces to build intuition about the real world
Monte Carlo simulation
- You’ll hear the term “Monte Carlo” thrown around when people talk about simulation
- Monte Carlo simulation template
- define some domain of possible inputs
- use a probability distribution to generate inputs over the domain
- perform some deterministic computation on the inputs
- aggregate the results
- Monte Carlo simulations rely on the Law of Large Numbers
- the average of a large sample ought to be close to the expected value
- the larger the sample, the closer it tends to be
Light at Night
- Researchers interested in the “link between the molecular circadian clock & metabolism”
- The study data shows that body mass of mice in “bright” group increased by about 3 grams more, on average, than mice in “dark” group
- Q: is this a meaningful change, or just randomness?
- Q: If it were all just randomness, how could we simulate outcomes that might be observed by chance alone?
- Without a computer?
- With a computer?
# data intake
NightLightRaw <- read_csv("LightatNight.csv", col_names = TRUE)
Parsed with column specification:
cols(
Light = [31mcol_character()[39m,
BodyMass0 = [32mcol_double()[39m,
BodyMass8 = [32mcol_double()[39m,
BMChange = [32mcol_double()[39m,
Corticosterone = [32mcol_double()[39m,
DayPct = [32mcol_double()[39m,
Consumption = [32mcol_double()[39m,
GlucoseInt = [31mcol_character()[39m,
GTT15 = [32mcol_double()[39m,
GTT120 = [32mcol_double()[39m,
Activity = [32mcol_double()[39m
)
# subsetting for comparison of interest
NightLight <-
NightLightRaw %>%
filter(Light %in% c("bright", "dark")) %>%
select(Light, BMChange)
# summary statistics for each group
favstats(BMChange ~ Light, data = NightLight)
ObsMean <- mean(BMChange ~ Light, data = NightLight, na.rm = TRUE)
ObsMeanDiff <- ObsMean["bright"] - ObsMean["dark"]
Light at Night Simulation
# simulate with mosaic::do()
NightLightSims <-
mosaic::do(1000) *
NightLight %>%
mutate(Light = shuffle(Light)) %>%
group_by(Light) %>%
summarise(meanBMChange = mean(BMChange, na.rm = TRUE))
# results after `shuffle( )`
favstats(meanBMChange ~ Light, data = NightLightSims)
# mean differences for shuffled data
LightSimResults <-
NightLightSims %>%
select(-.row) %>%
spread(key = Light, value = meanBMChange) %>%
mutate(meanDiff = bright - dark)
# distribution of outcomes expected by chance (random simulations)
p <-
LightSimResults %>%
ggplot(aes(x = meanDiff)) +
geom_density()
p
# comparison with observed outcome
p + geom_vline(xintercept = ObsMeanDiff)
Back to NCI60
- Cancer types are often named for the location where they’re found
- lung,
- ovarian,
- breast,
- etc
- Problem: tissue of origin may not be the best indicator for appropriate treatment
- Previously, we used unsupervised learning methods to explore relationships among various cancer cell lines
- Good idea, but it’s a challenging problem
- It’s hard to say if we found much of value
- all cells have a genome with lots of different genes that the behavior
- microarrays are used to examine the expression of individual genes within a cell
- each microarray has many (dozens; hundreds; thousands) probes that provide a snapshot of gene activity
- comparisons among cells in different states can reveal clues about which genes are linked to different aspects of cell activity
- Problem:
- most genes regulate mundane behaviors typical of all cells… we don’t care about these
- some genes regulate problem behaviors related to cancer cells (e.g., over-rapid reproduction)
- we want to separate the vital few genes from the trivial many…
- Q: Any good ideas?
NCI60 data reduction
- Goal: we want to separate the vital few genes from the trivial many
- most genes regulate mundane behaviors typical of all cells… we don’t care about these
- some genes regulate problem behaviors related to cancer cells (e.g., over-rapid reproduction)
- Simple Idea:
- if expression of a probe is the same across all available cancer samples, it isn’t to differentiate them!
- Q: How can we tell if a probe is the same across all available cancer samples?
- Q: How will we separate the vital few from the trivial many?
NCI60 data reduction
- for now, we start with transposed NCI60 data
- probes are the cases
- cell lines are the variables
- this is the opposite of the form we used for our cluster analysis
- Goal: we want to calculate the SD for each probe among all cell lines
- High variability across cell lines suggests the probe might be useful to differentiate among cancer types
- Q: How will we know how high is high enough?
- Q: How could you (hypothetically) solve this without a computer?
NCI60 <- read_csv("NCI60.csv")
Parsed with column specification:
cols(
.default = col_double(),
Probe = [31mcol_character()[39m
)
See spec(...) for full column specifications.
head(NCI60)
Spreads <-
NCI60 %>%
gather(value = expression, key = cellLine, -Probe) %>%
group_by(Probe) %>%
summarise(N = n(),
spread = sd(expression)) %>%
arrange(desc(spread)) %>%
mutate(order = row_number())
Simulations for NCI60 data reduction
- We want to simulate a null distribution such that:
- we break any association between probe labels and gene expression measurements
- All that remains is random variability
- we then compare observed results with the random variability
- Q: How does the code shown accomplish this task?
- Q: What conclusions can you draw as a result?
- Q: Describe similarities & differences between this example & the process used in “Light at Night”?
Sim_spreads <-
NCI60 %>%
gather(value = expression, key = cellLine, -Probe) %>%
mutate(Probe = shuffle(Probe)) %>%
group_by(Probe) %>%
summarise(N = n(),
spread = sd(expression)) %>%
mutate(order = row_number())
threshold <- max(Sim_spreads$spread)
Spreads %>%
filter(order <= 500) %>%
ggplot(aes(x = order, y = spread)) +
ylim(limits = c(2.5, 7.5)) +
geom_line(size = 1) +
geom_hline(yintercept = threshold, color = "red", size = 1, linetype = 2) +
annotate("text", x = 400, y = threshold + 0.2, label = "Largest random outcome")

# head(Spreads, 10)
Randomness is the key to simulation
- random number generation is the workhorse of simulation
- Let’s TRY IT!
- write down a series of twenty 0’s and 1’s in random order
- as random as you can
- just use your brain, nothing else
- after you finish, let R try:
resample(0:1, 20)
- Carefully inspect the results you and your neighbor created… is there anything surprising?
- Q: What are some other ways to generate truly random outcomes without a computer
- Binary outcome (T/F)
- Random assignment into three groups (equal in size)
- Random assignment into three groups (NOT equal in size)
- Random 10-digit number
- Sample with replacement from a set of 43 measurements (e.g., each is the weight of an object)
Random number generators
- random isn’t haphazard… random has “structure”
- random number generators are the workhorse of simulation
- hardware (true) random number generators
- pseudo random number generators
- surprisingly challenging to manufacture randomness!
- 1996: Diehard tests
- 2007: TestU01
- Small Crush (10 tests)
- Crush (96 tests)
- Big crush (160 tests)
Popular randomizing functions available in R
runif( )
sample( )
shuffle( )
resample( )
- name-brand probability distributions
sample(c("red", "blue", TRUE, 2.71), size = 1)
[1] "2.71"
sample(1:12, size = 1)
[1] 7
sample(letters, size = 1)
[1] "n"
sample(LETTERS, size = 1)
[1] "V"
Brush with destiny
- Ever wonder how chance encounters might shape the rest of your life?
- There is someone at Penn State who is the very closest personal match for you among those you’ve never met
- soulmate
- greatest friend you could ever know
- potential spouse
- ideal business partner
- whatever floats your boat (and theirs, apparently!)
- You don’t know this person, but you both always buy coffee at the HUB Bookstore between classes on MWF
- You both have a class break from about 10 to 11am
- You both swing by Starbucks sometime during that break
- Goal: What’s the chance that the two of you meet?
- Q: How can you simulate this scenario?
Simulation
n <- 10000
sim_meet <- data.frame(
you <- runif(n, min = 0, max = 60),
destiny <- runif(n, min = 0, max = 60)) %>%
mutate(result = ifelse(abs(you - destiny) <= 10,
"Same time, same place!", "So close, and yet so far..."))
tally(~ result, format = "percent", data = sim_meet)
result
Same time, same place! So close, and yet so far...
30.37 69.63
destinyModel <- binom.test(~ result, n, success = "Same time, same place!", data = sim_meet)
confint(destinyModel)
sim_meet %>%
ggplot() +
geom_point(aes(x = destiny, y = you, color = result), alpha = 0.3) +
ylab("You arrive (minutes after 10am)") +
xlab("Destiny arrives (minutes after 10am)") +
ggtitle("(simulated) Probability you'll meet your destiny at Starbucks")

NA
Key principles in simulation
- design
- modularity
- replication
Key principles: Design
- start simple
- add complexity gradually as you build up toward a more realistic simulation
Brush with destiny design
- Starbucks arrival time is uniform between 10am & 11am (60 minute window) for both
- if you’re both there at the same time, you’ll definitely meet
- Q: how might you challenge these assumptions?
Key principles: Modularity
- often helpful to wrap up the simulation as a function
- can then call it repeatedly (without copy/paste)
- modify options and parameters as arguments to your function
- it pays to plan what features your simulation will need & consider splitting them off as different functions
- easy to reuse those functions in other simulations
- often easier to read the code
starbucks_sim <- function(num_sim = 1000, wait = 10) {
# purpose: simulate chance meeting between two people
### num_sim: number of times to replicate simulation
### wait: number of minutes between arrivals for successful meeting
sim_meet <- data.frame(
you <- runif(n, min = 0, max = 60),
destiny <- runif(n, min = 0, max = 60)) %>%
mutate(result = ifelse(abs(you - destiny) <= 10,
"Same time, same place!", "So close, and yet so far..."))
destinyModel <- binom.test(~ result, n, success = "Same time, same place!", data = sim_meet)
return(confint(destinyModel))
}
starbucks_sim()
Key principles: Reproducibility
starbucks_sim()
- uh oh… that’s not the same answer we had last time!
- reproducibility of simulations
- R functions like
runif( )
are called psuedo random because it’s deterministic if the initial state is known
- the initial state is called the “seed”
set.seed( )
lets the user define the initial state in R
- otherwise, R uses the system clock to choose a seed each time you run the code
# choose seed
set.seed(23) # set initial state of RNG seed
runif(n = 5) # new outcome
[1] 0.5766 0.2231 0.3319 0.7107 0.8194
runif(n = 5) # new outcome
[1] 0.4237 0.9635 0.9781 0.8405 0.9966
# this is why we say *psuedo* random
set.seed(23) # set initial state of RNG seed
runif(n = 5) # back to outcome 1 again
[1] 0.5766 0.2231 0.3319 0.7107 0.8194
# different seed
set.seed(301) # new seed
runif(n = 5) # new outcome
[1] 0.597106 0.132231 0.002137 0.753970 0.614147
Key principles: Replication
- how many times do we want to replicate this simulation?
- results will be sensitive to the number of computations that we’re willing to execute
- So, how many? It’s a balance:
- more simulated replications leads to greater precision of our estimates
- unnecessary calculations waste time and computing resources
- Q: compare the results shown
- what is
num_sims
vs n
?
- what should
mean
& sd
mean to us?
simple_sim <- function(num_sim = 1000, wait = 10) {
# purpose: lean version of `starbucks_sim` to explore replication
you <- runif(num_sim, min = 0, max = 60)
destiny <- runif(num_sim, min = 0, max = 60)
return(sum(abs(you - destiny) <= wait) / num_sim)
}
reps <- 2500
params <- data.frame(num_sims = c(100, 400, 1600))
sim_results <-
params %>%
group_by(num_sims) %>%
dplyr::do(mosaic::do(reps) * simple_sim(num_sim = .$num_sims, wait = 10))
|========================================================= | 67% ~1 s remaining
|======================================================================================|100% ~0 s remaining
favstats(simple_sim ~ num_sims, data = sim_results)
sim_results %>%
ggplot(aes(x = simple_sim, color = factor(num_sims))) +
geom_density(size = 2) +
scale_x_continuous("Proportion of times you meet")

Modifying the simulation of our Brush with Destiny
- Q: what’s the average time it takes to arrive at Starbucks after class?
- Q: what’s the average time it takes the other person???
- Q: By the way, what’s the probability you actually talk to this person if they are even there??
Modifying the simulation of our Brush with Destiny
Assumptions modified
- your arrival time is 5 minutes + random delays
- your minimum possible time is 5 minutes
- random delays:
rexp(n, .1)
- typically 10 minutes or less
- possibly longer
- never negative
- other person arrival time is unknown + random delays
- fastest arrival is unknown, we assume uniform between 3 & 20 minutes
- random delays:
rexp(n, .1)
- maybe 5% chance you’re both willing to strike up a meaningful conversation with a stranger at Starbucks on any given day
- Q: Share some additional things we still haven’t considered here!
- Note about modeling waiting time (i.e., delay)
- Lots of “name-brand” probability distributions model waiting time scenarios
- don’t get stuck on the details of that choice here, just pay attention in STAT 414!
- our choice:
ggplot() + geom_density(aes(x = rexp(n, .1)))
n <- 100000
sim_meetExp <- data.frame(
you <- 5 + rexp(n, 0.1), # your arrival time (5 min + random delay)
destiny <- runif(n, min = 3, max = 20) + # fastest arrival is unknown between 3 & 20 min
rexp(n, 0.1)) %>% # random delay
mutate(opportunity = ifelse(abs(you - destiny) <= 10,
"Same time, same place!", "So close, and yet so far..."),
talkativeMood = sample(c(TRUE, FALSE), size = n,
replace = TRUE, prob = c(0.05, 0.95)),
result = ifelse(test = (opportunity == "Same time, same place!" & talkativeMood == TRUE),
yes = "Bliss", no = "Oh well..."))
tally(~ result, format = "percent", data = sim_meetExp)
result
Bliss Oh well...
2.512 97.488
destinyModel <- binom.test(~ result, n, success = "Bliss", data = sim_meetExp)
confint(destinyModel)
sim_meetExp %>%
ggplot() +
geom_point(aes(x = destiny, y = you, color = result), alpha = 0.3) +
ylab("You arrive (minutes after 10am)") +
xlab("Destiny arrives (minutes after 10am)") +
ggtitle("This is why it's so hard to meet people...") +
scale_x_continuous(limits = c(0, 60)) +
scale_y_continuous(limits = c(0, 60))

NA
Simulating Models
- It’s often handy to directly simulate a statistical model of some kind in order to build intuition about it’s properties
- Simulate data that could have been produced by some model
- Simulate sensitivity to model assumptions
Simulating simple linear regression data
Consider a simple linear regression model:
\[y = \beta_0 + \beta_1*x\]
- such that
- Y is some measurable response variable that we want to predict
- X is some measurable explanatory variable used to predict Y
- again, we need to integrate some desired model with some “noise” due to randomness
- What will be fixed?
- What will be random?
Simulating simple linear regression data
Consider a logistic regression model:
\[E[Y | X] = \beta_0 + \beta_1*X\]
\[y_i = b_0 + b_1*x_i + \epsilon_i\]
- Q: What’s the difference between these representations?
- Q: Can you spot our inputs in the model summary?
- Q: What if we change the sample size?
# beta_0
intercept <- 26
# beta_1
beta <- -2.5
# sample size for our simulated model
n <- 5000
# X is a simulated random variable representing some measurable characteristic of the cases
xtest <- rnorm(n, mean = 10, sd = 1)
# simulate errors: assume i.i.d. N(0, sigma)
errors <- rnorm(n, mean = 0, sd = 5)
# Y is a simulated random variable representing our response
ytest <- intercept + (xtest * beta) + errors
# fit the simulated logistic regression model
simreg <- lm(ytest ~ xtest)
# how did we do?
msummary(simreg)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 25.0274 0.7180 34.9 <2e-16 ***
xtest -2.4117 0.0712 -33.9 <2e-16 ***
Residual standard error: 5.01 on 4998 degrees of freedom
Multiple R-squared: 0.187, Adjusted R-squared: 0.186
F-statistic: 1.15e+03 on 1 and 4998 DF, p-value: <2e-16
# confint(simreg)
Simulating logistic regression data
Consider a logistic regression model:
\[log(\frac{\pi}{1-\pi}) = \beta_0 + \beta_1*x\]
- such that,
- \(\pi\) represents the population probability of “success” according to some binary outcome of interest to us
- X is some measurable explanatory variable used to predict the odds of “success”
- again, we need to integrate some desired model with some “noise” due to randomness
- What will be fixed?
- What will be random?
Simulating logistic regression data
Consider a logistic regression model:
\[log(\frac{\pi}{1-\pi}) = \beta_0 + \beta_1*x\]
\[log(\frac{p_i}{1-p_i}) = b_0 + b_1*x_i\]
- Q: What’s the difference between these representations?
- Q: Why this step?
ifelse(runif(n) < prob, 1, 0)
# beta_0
intercept <- -1
# beta_1
beta <- 0.5
# sample size for our simulated model
n <- 5000
# X is a simulated random variable representing some measurable characteristic of the cases
xtest <- rnorm(n, mean = 1, sd = 1)
# recall: the log-odds is a *linear* model with respect to our explanatory/predictor variables
linpred <- intercept + (xtest * beta)
# transform predictions to probabilities
prob <- exp(linpred)/(1 + exp(linpred))
# Y is a simulated random variable representing our response; the "errors" are built-in here
ytest <- ifelse(runif(n) < prob, 1, 0)
# fit the simulated logistic regression model
logreg <- glm(ytest ~ xtest, family=binomial)
# how did we do?
coef(logreg)
(Intercept) xtest
-1.0136 0.5062
confint(logreg)
Waiting for profiling to be done...
2.5 % 97.5 %
(Intercept) -1.1048 -0.9238
xtest 0.4444 0.5687
## interpret odds
exp(coef(logreg))
(Intercept) xtest
0.3629 1.6590
exp(confint(logreg))
Waiting for profiling to be done...
2.5 % 97.5 %
(Intercept) 0.3313 0.397
xtest 1.5596 1.766
Evaluating Model Assumptions
- All models have assumptions of one sort or another
- When assumptions are violated, the conclusions of the model may be jeopardized
- Not all violations are equally dangerous…
- Q: How can we tell which is which??
Evaluating Simple Linear Regression Assumptions
- Q: What are the (4) assumptions of linear regression?
- Q: How might we use simulation to “challenge” some of these assumptions and see how sensitive they are?
Challenging the equal variance assumption
\[E[Y|X] = \beta_0 + \beta_1X_1 + \beta_2X_2\]
\[y_i = b_0 + b_1x_{1i} + b_2x_{2i} + \epsilon_i\]
- Q: How might we challenge the equal variance assumption?
- Q: What actually goes wrong when we have a problem of unequal variance?
# sample size for our simulation
n <- 250
# sd of model errors
rmse <- 1
# two explanatory variables
x1 <- runif(n, min = 0, max = 15)
# model coefficients
beta0 <- -3
beta1 <- 0.5
# simulate response data
y <- beta0 + beta1*x1 + rnorm(n, mean = 0, sd = rmse) # equal variance
# y <- beta0 + beta1*x1 + rnorm(n, mean = 0, sd = rmse + x1) # UNequal variance
# plot data with smoother
data.frame(y, x1) %>%
ggplot(aes(x = x1, y = y)) +
geom_point() +
geom_smooth()

# regression model
simLM <- lm(y ~ x1)
# check the model fit
msummary(simLM)
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.0248 0.1320 -22.9 <2e-16 ***
x1 0.4986 0.0149 33.4 <2e-16 ***
Residual standard error: 0.983 on 248 degrees of freedom
Multiple R-squared: 0.818, Adjusted R-squared: 0.817
F-statistic: 1.11e+03 on 1 and 248 DF, p-value: <2e-16
mplot(simLM)[1] # residual diagnostics
[[1]]

Simulate many models
- Q: What do expect the distribution of parameter estimates to look like?
- If equal variance assumption is satisfied
- If equal variance assumption is NOT satisfied
dosim <- function() {
y <- beta0 + beta1*x1 + rnorm(n, mean = 0, sd = rmse + x1) # departure
mod <- lm(y ~ x1)
result <- coefficients(mod)
return(result)
}
sims <- mosaic::do(1000) * dosim()
favstats(~ x1, data = sims)
# distribution coefficient estimates
sims %>%
ggplot(aes(x = x1)) +
geom_density() +
# pass arguments to `dnorm` function
stat_function(fun = dnorm, args = list(mean = mean(sims$x1), sd = sd(sims$x1)),
linetype = 2) +
ggtitle("distribution of regression parameter") +
scale_x_continuous("beta 1 coefficients")

Simulating a complex system
any_active <- function(df) {
# return TRUE if someone has not finished
return(max(df$endtime) == Inf)
}
next_customer <- function(df) {
# returns the next customer in line
res <- filter(df, endtime == Inf) %>%
arrange(arrival)
return(head(res, 1))
}
update_customer <- function(df, cust_num, end_time) {
# sets the end time of a specific customer
return(mutate(df, endtime = ifelse(custnum == cust_num, end_time, endtime)))
}
run_sim <- function(n = 1/2, m = 3/2, hours = 6) {
# simulation of bank where there is just one teller
# n: expected number of customers per minute
# m: expected length of transaction is m minutes
# hours: bank open for this many hours
customers <- rpois(hours * 60, lambda = n)
arrival <- numeric(sum(customers))
position <- 1
for (i in 1:length(customers)) {
numcust <- customers[i]
if (numcust != 0) {
arrival[position:(position + numcust - 1)] <- rep(i, numcust)
position <- position + numcust
}
}
duration <- rexp(length(arrival), rate = 1/m) # E[X]=m
df <- data.frame(arrival, duration, custnum = 1:length(duration),
endtime = Inf, stringsAsFactors = FALSE)
endtime <- 0 # set up beginning of simulation
while (any_active(df)) { # anyone left to serve
next_one <- next_customer(df)
now <- ifelse(next_one$arrival >= endtime, next_one$arrival, endtime)
endtime <- now + next_one$duration
df <- update_customer(df, next_one$custnum, endtime)
}
df <- mutate(df, totaltime = endtime - arrival)
return(favstats(~ totaltime, data = df))
}
sim_results <- mosaic::do(3) * run_sim()
sim_results
LS0tCnRpdGxlOiAiU2ltdWxhdGlvbiIKc3VidGl0bGU6ICJNRFNSIENoIDEwIgpvdXRwdXQ6IAogIHNsaWR5X3ByZXNlbnRhdGlvbjogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQgIAotLS0KCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCm9wdGlvbnMoZGlnaXRzPTQpCgojIHBhY2thZ2VzIHVzZWQKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgaW5wdXRzIHN1bW1hcnkKCgpgYGAKCgojIyBBZ2VuZGEKCgojIyMjIEFubm91bmNlbWVudHMKCi0gTURTUiBDaCAxMCBwcm9ncmFtbWluZyBub3RlYm9vayBhc3NpZ25lZAoKIyMjIyBNRFNSIENoIDEwIEVycmF0YSAvIFRpcHMKCi0gU29tZSBzZWN0aW9ucyBkb24ndCByZXF1aXJlIHByb2dyYW1taW5nLCBidXQgcGxlYXNlIHN0aWxsIGluY2x1ZGUgdGhlIGhlYWRlcnMgZm9yIG5hdmlnYXRpb24gcHVycG9zZXMKLSBwLiAyMzQtMjM1OiBSZW5hbWUgdGhlIHJlc3VsdCBmb3IgMjAsMDAwIHNpbXVsYXRpb25zIGFzIGBzaW1fcmVzdWx0czIwa2AKICAgIC0gdGhlIGF1dGhvcnMgcmV1c2UgdGhlIG9iamVjdCBuYW1lIGBzaW1fcmVzdWx0c2AgZm9yIHRoZSBleGFtcGxlIHdpdGggMjAsMDAwIHNpbXVsYXRpb25zLCBidXQgdGhpcyBvdmVyd3JpdGVzIHRoZSBvYmplY3QgZXhwZWN0ZWQgZm9yIHRoZSBwbG90IGF0IHRoZSB0b3Agb2YgcC4gMjM1LiAgCiAgICAtIGJ5IHJlbmFtaW5nIHRoZSBvYmplY3QgcmVzdWx0aW5nIGZvcm0gMjAsMDAwIHNpbXVsYXRpb25zLCB0aGUgZ2dwbG90IGNhbGwgd2lsbCBzdGlsbCBiZSBhYmxlIHRvIGFjY2VzcyB0aGUgaW50ZW5kZWQgb2JqZWN0CgoKIyMgUHVycG9zZXMgZm9yIFNpbXVsYXRpb24KCi0gUTogV2hhdCBjb21lcyB0byBtaW5kIHdoZW4geW91IHRoaW5rIG9mICJzaW11bGF0aW9uIgotIFE6IFdoYXQgYXJlIHRoZSBwdXJwb3NlcyBmb3Igc2ltdWxhdGlvbj8KCgojIyBJbnRybyB0byBTaW11bGF0aW9uCgotIFNpbXVsYXRpb25zIGFyZSBvZnRlbiB1c2VmdWwgdG8gbGVhcm4gYWJvdXQgdGhlIG5hdHVyZSBvZiBhIHJhbmRvbSBwcm9jZXNzCi0gV2UgYnVpbGQgYSByZWFsaXN0aWMgIm1vZGVsIiBvZiB0aGUgc3lzdGVtL3Byb2Nlc3MgdGhhdCB3ZSB3YW50IHRvIHN0dWR5CiAgICAtIFRoZSBhY3Qgb2YgYnVpbGRpbmcgaXRzZWxmIG9mdGVuIGxlYWRzIHRvIGRlZXBlciB1bmRlcnN0YW5kaW5nCiAgICAtIFdlIHN0dWR5IG91dGNvbWVzIHByb2R1Y2VkIGJ5IG91ciBtb2RlbCB0byBidWlsZCBpbnR1aXRpb24gYWJvdXQgdGhlIG5hdHVyZSBvZiB0aGUgc3lzdGVtIGFuZCBpdCdzIG91dGNvbWVzCi0gKipXaW5ub3dpbmcgb3V0IGh5cG90aGVzZXMqKjogCiAgICAtIHdlIG9mdGVuIGhhdmUgbWFueSBjb21wZXRpbmcgaHlwb3RoZXNlcyBhYm91dCBob3cgYSByZWFsIHN5c3RlbSB3b3JrcwogICAgLSB3ZSBjYW4gc2ltdWxhdGUvZ2VuZXJhdGUgZGF0YSBmcm9tIGVhY2ggaHlwb3RoZXNpcyBhbmQgcnVsZSBvdXQgdGhlIG9uZXMgdGhhdCBhcmVuJ3QgY29uc2lzdGVudCB3aXRoIHRoZSBvYnNlcnZlZCBkYXRhCiAgICAtIGUuZy4sIG91ciByYW5kb21pemF0aW9uIHRlc3QKLSAqKkNvbmRpdGlvbmFsIGluZmVyZW5jZSoqOiAKICAgIC0gYnVpbGQgYSBzeXN0ZW0gdGhhdCByZWZsZWN0cyBob3cgYSByZWFsIHN5c3RlbSB3b3JrcwogICAgLSB1c2UgdGhlIGRhdGEgdGhhdCBpdCBwcm9kdWNlcyB0byBidWlsZCBpbnR1aXRpb24gYWJvdXQgdGhlIHJlYWwgd29ybGQKCiMjIE1vbnRlIENhcmxvIHNpbXVsYXRpb24KCi0gWW91J2xsIGhlYXIgdGhlIHRlcm0gIk1vbnRlIENhcmxvIiB0aHJvd24gYXJvdW5kIHdoZW4gcGVvcGxlIHRhbGsgYWJvdXQgc2ltdWxhdGlvbgotIE1vbnRlIENhcmxvIHNpbXVsYXRpb24gdGVtcGxhdGUgCiAgICAtIGRlZmluZSBzb21lIGRvbWFpbiBvZiBwb3NzaWJsZSBpbnB1dHMKICAgIC0gdXNlIGEgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIHRvIGdlbmVyYXRlIGlucHV0cyBvdmVyIHRoZSBkb21haW4KICAgIC0gcGVyZm9ybSBzb21lIGRldGVybWluaXN0aWMgY29tcHV0YXRpb24gb24gdGhlIGlucHV0cwogICAgLSBhZ2dyZWdhdGUgdGhlIHJlc3VsdHMKLSBNb250ZSBDYXJsbyBzaW11bGF0aW9ucyByZWx5IG9uIHRoZSAqKkxhdyBvZiBMYXJnZSBOdW1iZXJzKiogCiAgICAtIHRoZSBhdmVyYWdlIG9mIGEgbGFyZ2Ugc2FtcGxlIG91Z2h0IHRvIGJlIGNsb3NlIHRvIHRoZSBleHBlY3RlZCB2YWx1ZSAKICAgIC0gdGhlIGxhcmdlciB0aGUgc2FtcGxlLCB0aGUgY2xvc2VyIGl0IHRlbmRzIHRvIGJlCgoKIyMgTGlnaHQgYXQgTmlnaHQKCiFbXShMaWdodGF0TmlnaHQucG5nKQoKLSBSZXNlYXJjaGVycyBpbnRlcmVzdGVkIGluIHRoZSAibGluayBiZXR3ZWVuIHRoZSBtb2xlY3VsYXIgY2lyY2FkaWFuIGNsb2NrICYgbWV0YWJvbGlzbSIKLSBUaGUgc3R1ZHkgZGF0YSBzaG93cyB0aGF0IGJvZHkgbWFzcyBvZiBtaWNlIGluICJicmlnaHQiIGdyb3VwIGluY3JlYXNlZCBieSBhYm91dCAzIGdyYW1zIG1vcmUsIG9uIGF2ZXJhZ2UsIHRoYW4gbWljZSBpbiAiZGFyayIgZ3JvdXAKICAgIC0gUTogaXMgdGhpcyBhIG1lYW5pbmdmdWwgY2hhbmdlLCBvciBqdXN0IHJhbmRvbW5lc3M/CiAgICAtIFE6IElmIGl0IHdlcmUgYWxsIGp1c3QgcmFuZG9tbmVzcywgaG93IGNvdWxkIHdlIHNpbXVsYXRlIG91dGNvbWVzIHRoYXQgbWlnaHQgYmUgb2JzZXJ2ZWQgYnkgY2hhbmNlIGFsb25lPwogICAgICAgIDEuICoqV2l0aG91dCoqIGEgY29tcHV0ZXI/CiAgICAgICAgMi4gV2l0aCBhIGNvbXB1dGVyPwoKYGBge3J9CiMgZGF0YSBpbnRha2UKTmlnaHRMaWdodFJhdyA8LSByZWFkX2NzdigiTGlnaHRhdE5pZ2h0LmNzdiIsIGNvbF9uYW1lcyA9IFRSVUUpCgojIHN1YnNldHRpbmcgZm9yIGNvbXBhcmlzb24gb2YgaW50ZXJlc3QKTmlnaHRMaWdodCA8LSAKICBOaWdodExpZ2h0UmF3ICU+JQogIGZpbHRlcihMaWdodCAlaW4lIGMoImJyaWdodCIsICJkYXJrIikpICU+JQogIHNlbGVjdChMaWdodCwgQk1DaGFuZ2UpCgojIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgZWFjaCBncm91cApmYXZzdGF0cyhCTUNoYW5nZSB+IExpZ2h0LCBkYXRhID0gTmlnaHRMaWdodCkKCk9ic01lYW4gPC0gbWVhbihCTUNoYW5nZSB+IExpZ2h0LCBkYXRhID0gTmlnaHRMaWdodCwgbmEucm0gPSBUUlVFKQpPYnNNZWFuRGlmZiA8LSBPYnNNZWFuWyJicmlnaHQiXSAtIE9ic01lYW5bImRhcmsiXQpgYGAKCiMjIExpZ2h0IGF0IE5pZ2h0IFNpbXVsYXRpb24KCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0KIyBzaW11bGF0ZSB3aXRoIG1vc2FpYzo6ZG8oKQpOaWdodExpZ2h0U2ltcyA8LSAKICBtb3NhaWM6OmRvKDEwMDApICogCiAgICBOaWdodExpZ2h0ICU+JQogICAgbXV0YXRlKExpZ2h0ID0gc2h1ZmZsZShMaWdodCkpICU+JQogICAgZ3JvdXBfYnkoTGlnaHQpICU+JQogICAgc3VtbWFyaXNlKG1lYW5CTUNoYW5nZSA9IG1lYW4oQk1DaGFuZ2UsIG5hLnJtID0gVFJVRSkpIAoKIyByZXN1bHRzIGFmdGVyIGBzaHVmZmxlKCApYApmYXZzdGF0cyhtZWFuQk1DaGFuZ2UgfiBMaWdodCwgZGF0YSA9IE5pZ2h0TGlnaHRTaW1zKQoKIyBtZWFuIGRpZmZlcmVuY2VzIGZvciBzaHVmZmxlZCBkYXRhCkxpZ2h0U2ltUmVzdWx0cyA8LSAKICBOaWdodExpZ2h0U2ltcyAlPiUKICBzZWxlY3QoLS5yb3cpICU+JQogIHNwcmVhZChrZXkgPSBMaWdodCwgdmFsdWUgPSBtZWFuQk1DaGFuZ2UpICU+JQogIG11dGF0ZShtZWFuRGlmZiA9IGJyaWdodCAtIGRhcmspIAoKCiMgZGlzdHJpYnV0aW9uIG9mIG91dGNvbWVzIGV4cGVjdGVkIGJ5IGNoYW5jZSAocmFuZG9tIHNpbXVsYXRpb25zKQpwIDwtIAogIExpZ2h0U2ltUmVzdWx0cyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtZWFuRGlmZikpICsgCiAgZ2VvbV9kZW5zaXR5KCkgCnAKCiMgY29tcGFyaXNvbiB3aXRoIG9ic2VydmVkIG91dGNvbWUKcCArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IE9ic01lYW5EaWZmKQogIApgYGAKCgoKIyMgQmFjayB0byBOQ0k2MAoKLSBDYW5jZXIgdHlwZXMgYXJlIG9mdGVuIG5hbWVkIGZvciB0aGUgbG9jYXRpb24gd2hlcmUgdGhleSdyZSBmb3VuZAogICAgLSBsdW5nLCAKICAgIC0gb3ZhcmlhbiwgCiAgICAtIGJyZWFzdCwgCiAgICAtIGV0YwotICoqUHJvYmxlbTogdGlzc3VlIG9mIG9yaWdpbiBtYXkgbm90IGJlIHRoZSBiZXN0IGluZGljYXRvciBmb3IgYXBwcm9wcmlhdGUgdHJlYXRtZW50KioKLSBQcmV2aW91c2x5LCB3ZSB1c2VkIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBtZXRob2RzIHRvIGV4cGxvcmUgcmVsYXRpb25zaGlwcyBhbW9uZyB2YXJpb3VzIGNhbmNlciBjZWxsIGxpbmVzIAogICAgLSBHb29kIGlkZWEsIGJ1dCBpdCdzIGEgY2hhbGxlbmdpbmcgcHJvYmxlbQogICAgLSBJdCdzIGhhcmQgdG8gc2F5IGlmIHdlIGZvdW5kIG11Y2ggb2YgdmFsdWUKLSBhbGwgY2VsbHMgaGF2ZSBhIGdlbm9tZSB3aXRoIGxvdHMgb2YgZGlmZmVyZW50IGdlbmVzIHRoYXQgdGhlIGJlaGF2aW9yCiAgICAtIG1pY3JvYXJyYXlzIGFyZSB1c2VkIHRvIGV4YW1pbmUgdGhlIGV4cHJlc3Npb24gb2YgaW5kaXZpZHVhbCBnZW5lcyB3aXRoaW4gYSBjZWxsCiAgICAgICAgLSBlYWNoIG1pY3JvYXJyYXkgaGFzIG1hbnkgKGRvemVuczsgaHVuZHJlZHM7IHRob3VzYW5kcykgcHJvYmVzIHRoYXQgcHJvdmlkZSBhIHNuYXBzaG90IG9mIGdlbmUgYWN0aXZpdHkKICAgICAgICAtIGNvbXBhcmlzb25zIGFtb25nIGNlbGxzIGluIGRpZmZlcmVudCBzdGF0ZXMgY2FuIHJldmVhbCBjbHVlcyBhYm91dCB3aGljaCBnZW5lcyBhcmUgbGlua2VkIHRvIGRpZmZlcmVudCBhc3BlY3RzIG9mIGNlbGwgYWN0aXZpdHkKLSBQcm9ibGVtOgogICAgLSBtb3N0IGdlbmVzIHJlZ3VsYXRlICoqbXVuZGFuZSoqIGJlaGF2aW9ycyB0eXBpY2FsIG9mIGFsbCBjZWxscy4uLiB3ZSBkb24ndCBjYXJlIGFib3V0IHRoZXNlCiAgICAtIHNvbWUgZ2VuZXMgcmVndWxhdGUgKipwcm9ibGVtKiogYmVoYXZpb3JzIHJlbGF0ZWQgdG8gY2FuY2VyIGNlbGxzIChlLmcuLCBvdmVyLXJhcGlkIHJlcHJvZHVjdGlvbikKICAgIC0gKip3ZSB3YW50IHRvIHNlcGFyYXRlIHRoZSB2aXRhbCBmZXcgZ2VuZXMgZnJvbSB0aGUgdHJpdmlhbCBtYW55Li4uKioKICAgIC0gUTogQW55IGdvb2QgaWRlYXM/CgojIyBOQ0k2MCBkYXRhIHJlZHVjdGlvbgoKLSBHb2FsOiB3ZSB3YW50IHRvIHNlcGFyYXRlIHRoZSB2aXRhbCBmZXcgZ2VuZXMgZnJvbSB0aGUgdHJpdmlhbCBtYW55IAogICAgLSBtb3N0IGdlbmVzIHJlZ3VsYXRlICoqbXVuZGFuZSoqIGJlaGF2aW9ycyB0eXBpY2FsIG9mIGFsbCBjZWxscy4uLiB3ZSBkb24ndCBjYXJlIGFib3V0IHRoZXNlCiAgICAtIHNvbWUgZ2VuZXMgcmVndWxhdGUgKipwcm9ibGVtKiogYmVoYXZpb3JzIHJlbGF0ZWQgdG8gY2FuY2VyIGNlbGxzIChlLmcuLCBvdmVyLXJhcGlkIHJlcHJvZHVjdGlvbikKLSBTaW1wbGUgSWRlYTogCiAgICAtIGlmIGV4cHJlc3Npb24gb2YgYSBwcm9iZSBpcyB0aGUgc2FtZSBhY3Jvc3MgYWxsIGF2YWlsYWJsZSBjYW5jZXIgc2FtcGxlcywgaXQgaXNuJ3QgdG8gZGlmZmVyZW50aWF0ZSB0aGVtIQogICAgLSBROiBIb3cgY2FuIHdlIHRlbGwgaWYgYSBwcm9iZSBpcyB0aGUgc2FtZSBhY3Jvc3MgYWxsIGF2YWlsYWJsZSBjYW5jZXIgc2FtcGxlcz8KICAgIC0gUTogSG93IHdpbGwgd2Ugc2VwYXJhdGUgdGhlIHZpdGFsIGZldyBmcm9tIHRoZSB0cml2aWFsIG1hbnk/IAoKIyMgTkNJNjAgZGF0YSByZWR1Y3Rpb24KCi0gZm9yIG5vdywgd2Ugc3RhcnQgd2l0aCB0cmFuc3Bvc2VkIE5DSTYwIGRhdGEgCiAgICAtIHByb2JlcyBhcmUgdGhlIGNhc2VzCiAgICAtIGNlbGwgbGluZXMgYXJlIHRoZSB2YXJpYWJsZXMKICAgIC0gdGhpcyBpcyB0aGUgb3Bwb3NpdGUgb2YgdGhlIGZvcm0gd2UgdXNlZCBmb3Igb3VyIGNsdXN0ZXIgYW5hbHlzaXMKLSBHb2FsOiB3ZSB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgU0QgZm9yIGVhY2ggcHJvYmUgYW1vbmcgYWxsIGNlbGwgbGluZXMKICAgIC0gSGlnaCB2YXJpYWJpbGl0eSBhY3Jvc3MgY2VsbCBsaW5lcyBzdWdnZXN0cyB0aGUgcHJvYmUgbWlnaHQgYmUgdXNlZnVsIHRvIGRpZmZlcmVudGlhdGUgYW1vbmcgY2FuY2VyIHR5cGVzCiAgICAtIFE6IEhvdyB3aWxsIHdlIGtub3cgaG93IGhpZ2ggaXMgaGlnaCBlbm91Z2g/CiAgICAtIFE6IEhvdyBjb3VsZCB5b3UgKGh5cG90aGV0aWNhbGx5KSBzb2x2ZSB0aGlzIHdpdGhvdXQgYSBjb21wdXRlcj8KCgpgYGB7cn0KTkNJNjAgPC0gcmVhZF9jc3YoIk5DSTYwLmNzdiIpIApoZWFkKE5DSTYwKQoKU3ByZWFkcyA8LSAKICBOQ0k2MCAlPiUKICBnYXRoZXIodmFsdWUgPSBleHByZXNzaW9uLCBrZXkgPSBjZWxsTGluZSwgLVByb2JlKSAlPiUKICBncm91cF9ieShQcm9iZSkgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCksIAogICAgICAgICAgICBzcHJlYWQgPSBzZChleHByZXNzaW9uKSkgJT4lCiAgYXJyYW5nZShkZXNjKHNwcmVhZCkpICU+JQogIG11dGF0ZShvcmRlciA9IHJvd19udW1iZXIoKSkKYGBgCgo8IS0tIERheTIgLS0+CgojIyBTaW11bGF0aW9ucyBmb3IgTkNJNjAgZGF0YSByZWR1Y3Rpb24KCi0gV2Ugd2FudCB0byBzaW11bGF0ZSBhIG51bGwgZGlzdHJpYnV0aW9uIHN1Y2ggdGhhdDoKICAgIC0gd2UgYnJlYWsgYW55IGFzc29jaWF0aW9uIGJldHdlZW4gcHJvYmUgbGFiZWxzIGFuZCBnZW5lIGV4cHJlc3Npb24gbWVhc3VyZW1lbnRzCiAgICAtIEFsbCB0aGF0IHJlbWFpbnMgaXMgcmFuZG9tIHZhcmlhYmlsaXR5CiAgICAtIHdlIHRoZW4gY29tcGFyZSBvYnNlcnZlZCByZXN1bHRzIHdpdGggdGhlIHJhbmRvbSB2YXJpYWJpbGl0eQotIFE6IEhvdyBkb2VzIHRoZSBjb2RlIHNob3duIGFjY29tcGxpc2ggdGhpcyB0YXNrPwotIFE6IFdoYXQgY29uY2x1c2lvbnMgY2FuIHlvdSBkcmF3IGFzIGEgcmVzdWx0PwotIFE6IERlc2NyaWJlIHNpbWlsYXJpdGllcyAmIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhpcyBleGFtcGxlICYgdGhlIHByb2Nlc3MgdXNlZCBpbiAiTGlnaHQgYXQgTmlnaHQiPwoKYGBge3J9ClNpbV9zcHJlYWRzIDwtIAogIE5DSTYwICU+JQogIGdhdGhlcih2YWx1ZSA9IGV4cHJlc3Npb24sIGtleSA9IGNlbGxMaW5lLCAtUHJvYmUpICU+JQogIG11dGF0ZShQcm9iZSA9IHNodWZmbGUoUHJvYmUpKSAlPiUKICBncm91cF9ieShQcm9iZSkgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCksIAogICAgICAgICAgICBzcHJlYWQgPSBzZChleHByZXNzaW9uKSkgJT4lCiAgbXV0YXRlKG9yZGVyID0gcm93X251bWJlcigpKQoKdGhyZXNob2xkIDwtIG1heChTaW1fc3ByZWFkcyRzcHJlYWQpCgpTcHJlYWRzICU+JQogIGZpbHRlcihvcmRlciA8PSA1MDApICU+JQogIGdncGxvdChhZXMoeCA9IG9yZGVyLCB5ID0gc3ByZWFkKSkgKyAKICB5bGltKGxpbWl0cyA9IGMoMi41LCA3LjUpKSArIAogIGdlb21fbGluZShzaXplID0gMSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0aHJlc2hvbGQsIGNvbG9yID0gInJlZCIsIHNpemUgPSAxLCBsaW5ldHlwZSA9IDIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gNDAwLCB5ID0gdGhyZXNob2xkICsgMC4yLCBsYWJlbCA9ICJMYXJnZXN0IHJhbmRvbSBvdXRjb21lIikKCiMgaGVhZChTcHJlYWRzLCAxMCkKCmBgYAoKPCEtLSBEYXkzIC0tPgoKIyMgUmFuZG9tbmVzcyBpcyB0aGUga2V5IHRvIHNpbXVsYXRpb24KCi0gcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIGlzIHRoZSB3b3JraG9yc2Ugb2Ygc2ltdWxhdGlvbgotIExldCdzIFRSWSBJVCEKICAgIDEuIHdyaXRlIGRvd24gYSBzZXJpZXMgb2YgdHdlbnR5IDAncyBhbmQgMSdzIGluIHJhbmRvbSBvcmRlciAKICAgICAgICAtIGFzIHJhbmRvbSBhcyB5b3UgY2FuCiAgICAgICAgLSBqdXN0IHVzZSB5b3VyIGJyYWluLCBub3RoaW5nIGVsc2UKICAgIDIuIGFmdGVyIHlvdSBmaW5pc2gsIGxldCBSIHRyeTogYHJlc2FtcGxlKDA6MSwgMjApYAogICAgMy4gQ2FyZWZ1bGx5IGluc3BlY3QgdGhlIHJlc3VsdHMgeW91IGFuZCB5b3VyIG5laWdoYm9yIGNyZWF0ZWQuLi4gaXMgdGhlcmUgYW55dGhpbmcgc3VycHJpc2luZz8KLSBROiBXaGF0IGFyZSBzb21lIG90aGVyIHdheXMgdG8gZ2VuZXJhdGUgdHJ1bHkgcmFuZG9tIG91dGNvbWVzIHdpdGhvdXQgYSBjb21wdXRlcgogICAgLSBCaW5hcnkgb3V0Y29tZSAoVC9GKQogICAgLSBSYW5kb20gYXNzaWdubWVudCBpbnRvIHRocmVlIGdyb3VwcyAoZXF1YWwgaW4gc2l6ZSkKICAgIC0gUmFuZG9tIGFzc2lnbm1lbnQgaW50byB0aHJlZSBncm91cHMgKE5PVCBlcXVhbCBpbiBzaXplKQogICAgLSBSYW5kb20gMTAtZGlnaXQgbnVtYmVyCiAgICAtIFNhbXBsZSB3aXRoIHJlcGxhY2VtZW50IGZyb20gYSBzZXQgb2YgNDMgbWVhc3VyZW1lbnRzIChlLmcuLCBlYWNoIGlzIHRoZSB3ZWlnaHQgb2YgYW4gb2JqZWN0KQoKIVtpbWFnZSBjcmVkaXQ6IGh0dHBzOi8vbS54a2NkLmNvbS8xMjEwL10ocmFuZG9tX3hrY2QxMjEwLnBuZykKCgojIyBSYW5kb20gbnVtYmVyIGdlbmVyYXRvcnMKCi0gcmFuZG9tIGlzbid0IGhhcGhhemFyZC4uLiByYW5kb20gaGFzICJzdHJ1Y3R1cmUiCi0gcmFuZG9tIG51bWJlciBnZW5lcmF0b3JzIGFyZSB0aGUgd29ya2hvcnNlIG9mIHNpbXVsYXRpb24KICAgIC0gaGFyZHdhcmUgKHRydWUpIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9ycwogICAgLSBwc2V1ZG8gcmFuZG9tIG51bWJlciBnZW5lcmF0b3JzIAotIHN1cnByaXNpbmdseSBjaGFsbGVuZ2luZyB0byBtYW51ZmFjdHVyZSByYW5kb21uZXNzIQogICAgLSAxOTk2OiBEaWVoYXJkIHRlc3RzCiAgICAgICAgLSBiYXR0ZXJ5IG9mIDE1IHRlc3RzCiAgICAgICAgLSA8aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGllaGFyZF90ZXN0cz4KICAgIC0gMjAwNzogVGVzdFUwMSAKICAgICAgICAtIFNtYWxsIENydXNoICgxMCB0ZXN0cykKICAgICAgICAtIENydXNoICg5NiB0ZXN0cykKICAgICAgICAtIEJpZyBjcnVzaCAoMTYwIHRlc3RzKQoKCiMjIFBvcHVsYXIgcmFuZG9taXppbmcgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBSCgotICoqYHJ1bmlmKCApYCoqCi0gYHNhbXBsZSggKWAKLSBgc2h1ZmZsZSggKWAKLSBgcmVzYW1wbGUoIClgCi0gbmFtZS1icmFuZCBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zCiAgICAtIGBybm9ybSggKWAKICAgIC0gYHJiaW5vbSggKWAKICAgIC0gZXRjLi4uCgpgYGB7cn0Kc2FtcGxlKGMoInJlZCIsICJibHVlIiwgVFJVRSwgMi43MSksIHNpemUgPSAxKQpzYW1wbGUoMToxMiwgc2l6ZSA9IDEpCnNhbXBsZShsZXR0ZXJzLCBzaXplID0gMSkKc2FtcGxlKExFVFRFUlMsIHNpemUgPSAxKQoKYGBgCgoKIyMgbm90ZSBhYm91dCB1bmlmb3JtWzAsIDFdIAoKLSBnZW5lcmF0aW5nIHVuaWZvcm0gcmFuZG9tIG51bWJlcnMgYXJlIHN1cGVyIGltcG9ydGFudCAKLSBhbnkgcmFuZG9tIHNjaGVtZSB5b3Ugd2FudCB0byBjcmVhdGUgY2FuIGFsbW9zdCBjZXJ0YWlubHkgYnVpbHQgdXAgZnJvbSByYW5kb20gdW5pZm9ybQogICAgLSBROiBjb2luIGZsaXBzPwogICAgLSBROiByYW5kb21seSBzZWxlY3QgMjAlIG9mIHNvbWUgc2V0PwogICAgLSBROiBzYW1wbGUgZnJvbSBhIE5vcm1hbCBkaXN0cmlidXRpb24/CgoKCiMjIEJydXNoIHdpdGggZGVzdGlueQoKLSBFdmVyIHdvbmRlciBob3cgY2hhbmNlIGVuY291bnRlcnMgbWlnaHQgc2hhcGUgdGhlIHJlc3Qgb2YgeW91ciBsaWZlPwotIFRoZXJlIGlzIHNvbWVvbmUgYXQgUGVubiBTdGF0ZSB3aG8gaXMgdGhlIHZlcnkgY2xvc2VzdCBwZXJzb25hbCBtYXRjaCBmb3IgeW91IGFtb25nIHRob3NlIHlvdSd2ZSBuZXZlciBtZXQKICAgIC0gc291bG1hdGUKICAgIC0gZ3JlYXRlc3QgZnJpZW5kIHlvdSBjb3VsZCBldmVyIGtub3cKICAgIC0gcG90ZW50aWFsIHNwb3VzZQogICAgLSBpZGVhbCBidXNpbmVzcyBwYXJ0bmVyIAogICAgLSB3aGF0ZXZlciBmbG9hdHMgeW91ciBib2F0IChhbmQgdGhlaXJzLCBhcHBhcmVudGx5ISkKLSBZb3UgZG9uJ3Qga25vdyB0aGlzIHBlcnNvbiwgYnV0IHlvdSBib3RoIGFsd2F5cyBidXkgY29mZmVlIGF0IHRoZSBIVUIgQm9va3N0b3JlIGJldHdlZW4gY2xhc3NlcyBvbiBNV0YKICAgIC0gWW91IGJvdGggaGF2ZSBhIGNsYXNzIGJyZWFrIGZyb20gYWJvdXQgMTAgdG8gMTFhbQogICAgLSBZb3UgYm90aCBzd2luZyBieSBTdGFyYnVja3Mgc29tZXRpbWUgZHVyaW5nIHRoYXQgYnJlYWsKLSBHb2FsOiBXaGF0J3MgdGhlIGNoYW5jZSB0aGF0IHRoZSB0d28gb2YgeW91IG1lZXQ/CiAgICAtIFE6IEhvdyBjYW4geW91IHNpbXVsYXRlIHRoaXMgc2NlbmFyaW8/ICAKCgoKIyMgU2ltdWxhdGlvbgoKYGBge3J9Cm4gPC0gMTAwMDAKc2ltX21lZXQgPC0gZGF0YS5mcmFtZSgKICB5b3UgPC0gcnVuaWYobiwgbWluID0gMCwgbWF4ID0gNjApLCAKICBkZXN0aW55IDwtIHJ1bmlmKG4sIG1pbiA9IDAsIG1heCA9IDYwKSkgJT4lCiAgbXV0YXRlKHJlc3VsdCA9IGlmZWxzZShhYnMoeW91IC0gZGVzdGlueSkgPD0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgIlNhbWUgdGltZSwgc2FtZSBwbGFjZSEiLCAiU28gY2xvc2UsIGFuZCB5ZXQgc28gZmFyLi4uIikpCgp0YWxseSh+IHJlc3VsdCwgZm9ybWF0ID0gInBlcmNlbnQiLCBkYXRhID0gc2ltX21lZXQpCgpkZXN0aW55TW9kZWwgPC0gYmlub20udGVzdCh+IHJlc3VsdCwgbiwgc3VjY2VzcyA9ICJTYW1lIHRpbWUsIHNhbWUgcGxhY2UhIiwgZGF0YSA9IHNpbV9tZWV0KQpjb25maW50KGRlc3RpbnlNb2RlbCkKYGBgCgoKYGBge3J9CnNpbV9tZWV0ICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRlc3RpbnksIHkgPSB5b3UsIGNvbG9yID0gcmVzdWx0KSwgYWxwaGEgPSAwLjMpICsgCiAgeWxhYigiWW91IGFycml2ZSAobWludXRlcyBhZnRlciAxMGFtKSIpICsgCiAgeGxhYigiRGVzdGlueSBhcnJpdmVzIChtaW51dGVzIGFmdGVyIDEwYW0pIikgKyAKICBnZ3RpdGxlKCIoc2ltdWxhdGVkKSBQcm9iYWJpbGl0eSB5b3UnbGwgbWVldCB5b3VyIGRlc3RpbnkgYXQgU3RhcmJ1Y2tzIikKICAKYGBgCgo8IS0tIERheTQgLS0+CgojIyBLZXkgcHJpbmNpcGxlcyBpbiBzaW11bGF0aW9uCgotIGRlc2lnbgotIG1vZHVsYXJpdHkKLSByZXBsaWNhdGlvbgoKCgojIyBLZXkgcHJpbmNpcGxlczogRGVzaWduIAoKLSBzdGFydCBzaW1wbGUgCi0gYWRkIGNvbXBsZXhpdHkgZ3JhZHVhbGx5IGFzIHlvdSBidWlsZCB1cCB0b3dhcmQgYSBtb3JlIHJlYWxpc3RpYyBzaW11bGF0aW9uCgojIyMjIEJydXNoIHdpdGggZGVzdGlueSBkZXNpZ24KCi0gU3RhcmJ1Y2tzIGFycml2YWwgdGltZSBpcyAqKnVuaWZvcm0qKiBiZXR3ZWVuIDEwYW0gJiAxMWFtICg2MCBtaW51dGUgd2luZG93KSBmb3IgYm90aAotIGlmIHlvdSdyZSBib3RoIHRoZXJlIGF0IHRoZSBzYW1lIHRpbWUsIHlvdSdsbCBkZWZpbml0ZWx5IG1lZXQKLSBROiBob3cgbWlnaHQgeW91IGNoYWxsZW5nZSB0aGVzZSBhc3N1bXB0aW9ucz8KCgojIyBLZXkgcHJpbmNpcGxlczogTW9kdWxhcml0eQoKLSBvZnRlbiBoZWxwZnVsIHRvIHdyYXAgdXAgdGhlIHNpbXVsYXRpb24gYXMgYSBmdW5jdGlvbgogICAgLSBjYW4gdGhlbiBjYWxsIGl0IHJlcGVhdGVkbHkgKHdpdGhvdXQgY29weS9wYXN0ZSkKICAgIC0gbW9kaWZ5IG9wdGlvbnMgYW5kIHBhcmFtZXRlcnMgYXMgYXJndW1lbnRzIHRvIHlvdXIgZnVuY3Rpb24KLSBpdCBwYXlzIHRvIHBsYW4gd2hhdCBmZWF0dXJlcyB5b3VyIHNpbXVsYXRpb24gd2lsbCBuZWVkICYgY29uc2lkZXIgc3BsaXR0aW5nIHRoZW0gb2ZmIGFzIGRpZmZlcmVudCBmdW5jdGlvbnMKICAgIC0gZWFzeSB0byByZXVzZSB0aG9zZSBmdW5jdGlvbnMgaW4gb3RoZXIgc2ltdWxhdGlvbnMKICAgIC0gb2Z0ZW4gZWFzaWVyIHRvIHJlYWQgdGhlIGNvZGUKCmBgYHtyfQpzdGFyYnVja3Nfc2ltIDwtIGZ1bmN0aW9uKG51bV9zaW0gPSAxMDAwLCB3YWl0ID0gMTApIHsgCiAgIyBwdXJwb3NlOiBzaW11bGF0ZSBjaGFuY2UgbWVldGluZyBiZXR3ZWVuIHR3byBwZW9wbGUgCiAgIyMjIG51bV9zaW06IG51bWJlciBvZiB0aW1lcyB0byByZXBsaWNhdGUgc2ltdWxhdGlvbgogICMjIyB3YWl0OiBudW1iZXIgb2YgbWludXRlcyBiZXR3ZWVuIGFycml2YWxzIGZvciBzdWNjZXNzZnVsIG1lZXRpbmcKc2ltX21lZXQgPC0gZGF0YS5mcmFtZSgKICB5b3UgPC0gcnVuaWYobiwgbWluID0gMCwgbWF4ID0gNjApLCAKICBkZXN0aW55IDwtIHJ1bmlmKG4sIG1pbiA9IDAsIG1heCA9IDYwKSkgJT4lCiAgbXV0YXRlKHJlc3VsdCA9IGlmZWxzZShhYnMoeW91IC0gZGVzdGlueSkgPD0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgIlNhbWUgdGltZSwgc2FtZSBwbGFjZSEiLCAiU28gY2xvc2UsIGFuZCB5ZXQgc28gZmFyLi4uIikpCgpkZXN0aW55TW9kZWwgPC0gYmlub20udGVzdCh+IHJlc3VsdCwgbiwgc3VjY2VzcyA9ICJTYW1lIHRpbWUsIHNhbWUgcGxhY2UhIiwgZGF0YSA9IHNpbV9tZWV0KQpyZXR1cm4oY29uZmludChkZXN0aW55TW9kZWwpKQp9CgpzdGFyYnVja3Nfc2ltKCkKCmBgYAoKCiMjIEtleSBwcmluY2lwbGVzOiBSZXByb2R1Y2liaWxpdHkKCmBgYHtyfQpzdGFyYnVja3Nfc2ltKCkKYGBgCgotIHVoIG9oLi4uIHRoYXQncyBub3QgdGhlIHNhbWUgYW5zd2VyIHdlIGhhZCBsYXN0IHRpbWUhCi0gcmVwcm9kdWNpYmlsaXR5IG9mIHNpbXVsYXRpb25zCiAgICAtIFIgZnVuY3Rpb25zIGxpa2UgYHJ1bmlmKCApYCBhcmUgY2FsbGVkICpwc3VlZG8qIHJhbmRvbSBiZWNhdXNlIGl0J3MgZGV0ZXJtaW5pc3RpYyBpZiB0aGUgaW5pdGlhbCBzdGF0ZSBpcyBrbm93bgogICAgLSB0aGUgaW5pdGlhbCBzdGF0ZSBpcyBjYWxsZWQgdGhlICJzZWVkIgogICAgLSBgc2V0LnNlZWQoIClgIGxldHMgdGhlIHVzZXIgZGVmaW5lIHRoZSBpbml0aWFsIHN0YXRlIGluIFIKICAgIC0gb3RoZXJ3aXNlLCBSIHVzZXMgdGhlIHN5c3RlbSBjbG9jayB0byBjaG9vc2UgYSBzZWVkIGVhY2ggdGltZSB5b3UgcnVuIHRoZSBjb2RlCgpgYGB7cn0KIyBjaG9vc2Ugc2VlZApzZXQuc2VlZCgyMykgICMgc2V0IGluaXRpYWwgc3RhdGUgb2YgUk5HIHNlZWQKcnVuaWYobiA9IDUpICAjIG5ldyBvdXRjb21lCnJ1bmlmKG4gPSA1KSAgIyBuZXcgb3V0Y29tZQoKIyB0aGlzIGlzIHdoeSB3ZSBzYXkgKnBzdWVkbyogcmFuZG9tCnNldC5zZWVkKDIzKSAgIyBzZXQgaW5pdGlhbCBzdGF0ZSBvZiBSTkcgc2VlZApydW5pZihuID0gNSkgICMgYmFjayB0byBvdXRjb21lIDEgYWdhaW4KCiMgZGlmZmVyZW50IHNlZWQKc2V0LnNlZWQoMzAxKSAjIG5ldyBzZWVkCnJ1bmlmKG4gPSA1KSAgIyBuZXcgb3V0Y29tZQoKYGBgCgoKIyMgS2V5IHByaW5jaXBsZXM6IFJlcGxpY2F0aW9uCgotIGhvdyBtYW55IHRpbWVzIGRvIHdlIHdhbnQgdG8gcmVwbGljYXRlIHRoaXMgc2ltdWxhdGlvbj8gCi0gcmVzdWx0cyB3aWxsIGJlIHNlbnNpdGl2ZSB0byB0aGUgbnVtYmVyIG9mIGNvbXB1dGF0aW9ucyB0aGF0IHdlJ3JlIHdpbGxpbmcgdG8gZXhlY3V0ZQotIFNvLCBob3cgbWFueT8gIEl0J3MgYSBiYWxhbmNlOgogICAgLSBtb3JlIHNpbXVsYXRlZCByZXBsaWNhdGlvbnMgbGVhZHMgdG8gZ3JlYXRlciBwcmVjaXNpb24gb2Ygb3VyIGVzdGltYXRlcwogICAgLSB1bm5lY2Vzc2FyeSBjYWxjdWxhdGlvbnMgd2FzdGUgdGltZSBhbmQgY29tcHV0aW5nIHJlc291cmNlcwotIFE6IGNvbXBhcmUgdGhlIHJlc3VsdHMgc2hvd24KICAgIC0gd2hhdCBpcyBgbnVtX3NpbXNgIHZzIGBuYD8KICAgIC0gd2hhdCBzaG91bGQgYG1lYW5gICYgYHNkYCBtZWFuIHRvIHVzPwoKCmBgYHtyfQpzaW1wbGVfc2ltIDwtIGZ1bmN0aW9uKG51bV9zaW0gPSAxMDAwLCB3YWl0ID0gMTApIHsgCiAgIyBwdXJwb3NlOiBsZWFuIHZlcnNpb24gb2YgYHN0YXJidWNrc19zaW1gIHRvIGV4cGxvcmUgcmVwbGljYXRpb24gIAogIHlvdSA8LSBydW5pZihudW1fc2ltLCBtaW4gPSAwLCBtYXggPSA2MCkKICBkZXN0aW55IDwtIHJ1bmlmKG51bV9zaW0sIG1pbiA9IDAsIG1heCA9IDYwKQogIHJldHVybihzdW0oYWJzKHlvdSAtIGRlc3RpbnkpIDw9IHdhaXQpIC8gbnVtX3NpbSkKfQoKcmVwcyA8LSAyNTAwCnBhcmFtcyA8LSBkYXRhLmZyYW1lKG51bV9zaW1zID0gYygxMDAsIDQwMCwgMTYwMCkpCnNpbV9yZXN1bHRzIDwtIAogIHBhcmFtcyAlPiUKICBncm91cF9ieShudW1fc2ltcykgJT4lCiAgZHBseXI6OmRvKG1vc2FpYzo6ZG8ocmVwcykgKiBzaW1wbGVfc2ltKG51bV9zaW0gPSAuJG51bV9zaW1zLCB3YWl0ID0gMTApKQpmYXZzdGF0cyhzaW1wbGVfc2ltIH4gbnVtX3NpbXMsIGRhdGEgPSBzaW1fcmVzdWx0cykKYGBgCgpgYGB7cn0Kc2ltX3Jlc3VsdHMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc2ltcGxlX3NpbSwgY29sb3IgPSBmYWN0b3IobnVtX3NpbXMpKSkgKyAKICBnZW9tX2RlbnNpdHkoc2l6ZSA9IDIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKCJQcm9wb3J0aW9uIG9mIHRpbWVzIHlvdSBtZWV0IikKYGBgCgoKCgojIyBNb2RpZnlpbmcgdGhlIHNpbXVsYXRpb24gb2Ygb3VyIEJydXNoIHdpdGggRGVzdGlueQoKLSBROiB3aGF0J3MgdGhlIGF2ZXJhZ2UgdGltZSBpdCB0YWtlcyB0byBhcnJpdmUgYXQgU3RhcmJ1Y2tzIGFmdGVyIGNsYXNzPwotIFE6IHdoYXQncyB0aGUgYXZlcmFnZSB0aW1lIGl0IHRha2VzIHRoZSBvdGhlciBwZXJzb24/Pz8KLSBROiBCeSB0aGUgd2F5LCB3aGF0J3MgdGhlIHByb2JhYmlsaXR5IHlvdSBhY3R1YWxseSB0YWxrIHRvIHRoaXMgcGVyc29uIGlmIHRoZXkgYXJlIGV2ZW4gdGhlcmU/PwoKIyMgTW9kaWZ5aW5nIHRoZSBzaW11bGF0aW9uIG9mIG91ciBCcnVzaCB3aXRoIERlc3RpbnkKCiMjIyMgQXNzdW1wdGlvbnMgbW9kaWZpZWQKCjEuIHlvdXIgYXJyaXZhbCB0aW1lIGlzIDUgbWludXRlcyArIHJhbmRvbSBkZWxheXMgCiAgICAtIHlvdXIgbWluaW11bSBwb3NzaWJsZSB0aW1lIGlzIDUgbWludXRlcwogICAgLSByYW5kb20gZGVsYXlzOiBgcmV4cChuLCAuMSlgIAogICAgICAgIC0gdHlwaWNhbGx5IDEwIG1pbnV0ZXMgb3IgbGVzcwogICAgICAgIC0gcG9zc2libHkgbG9uZ2VyIAogICAgICAgIC0gbmV2ZXIgbmVnYXRpdmUKMi4gb3RoZXIgcGVyc29uIGFycml2YWwgdGltZSBpcyB1bmtub3duICsgcmFuZG9tIGRlbGF5cwogICAgLSBmYXN0ZXN0IGFycml2YWwgaXMgdW5rbm93biwgd2UgYXNzdW1lIHVuaWZvcm0gYmV0d2VlbiAzICYgMjAgbWludXRlcwogICAgLSByYW5kb20gZGVsYXlzOiBgcmV4cChuLCAuMSlgIAozLiBtYXliZSA1JSBjaGFuY2UgeW91J3JlIGJvdGggd2lsbGluZyB0byBzdHJpa2UgdXAgYSBtZWFuaW5nZnVsIGNvbnZlcnNhdGlvbiB3aXRoIGEgc3RyYW5nZXIgYXQgU3RhcmJ1Y2tzIG9uIGFueSBnaXZlbiBkYXkKNC4gUTogU2hhcmUgc29tZSBhZGRpdGlvbmFsIHRoaW5ncyB3ZSBzdGlsbCAqKmhhdmVuJ3QqKiBjb25zaWRlcmVkIGhlcmUhCi0gTm90ZSBhYm91dCBtb2RlbGluZyB3YWl0aW5nIHRpbWUgKGkuZS4sIGRlbGF5KQogICAgLSBMb3RzIG9mICJuYW1lLWJyYW5kIiBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zIG1vZGVsIHdhaXRpbmcgdGltZSBzY2VuYXJpb3MKICAgIC0gZG9uJ3QgZ2V0IHN0dWNrIG9uIHRoZSBkZXRhaWxzIG9mIHRoYXQgY2hvaWNlIGhlcmUsIGp1c3QgcGF5IGF0dGVudGlvbiBpbiBTVEFUIDQxNCEKICAgIC0gb3VyIGNob2ljZTogYGdncGxvdCgpICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gcmV4cChuLCAuMSkpKWAKCgpgYGB7cn0KbiA8LSAxMDAwMDAKc2ltX21lZXRFeHAgPC0gZGF0YS5mcmFtZSgKICB5b3UgPC0gNSArIHJleHAobiwgMC4xKSwgICAgICAgICAgICAgICAgICAgICAgIyB5b3VyIGFycml2YWwgdGltZSAoNSBtaW4gKyByYW5kb20gZGVsYXkpCiAgZGVzdGlueSA8LSBydW5pZihuLCBtaW4gPSAzLCBtYXggPSAyMCkgKyAgICAgICMgZmFzdGVzdCBhcnJpdmFsIGlzIHVua25vd24gYmV0d2VlbiAzICYgMjAgbWluCiAgICAgICAgICAgICByZXhwKG4sIDAuMSkpICU+JSAgICAgICAgICAgICAgICAgICMgcmFuZG9tIGRlbGF5CiAgbXV0YXRlKG9wcG9ydHVuaXR5ID0gaWZlbHNlKGFicyh5b3UgLSBkZXN0aW55KSA8PSAxMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSB0aW1lLCBzYW1lIHBsYWNlISIsICJTbyBjbG9zZSwgYW5kIHlldCBzbyBmYXIuLi4iKSwgCiAgICAgICAgIHRhbGthdGl2ZU1vb2QgPSBzYW1wbGUoYyhUUlVFLCBGQUxTRSksIHNpemUgPSBuLCAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC4wNSwgMC45NSkpLAogICAgICAgICByZXN1bHQgPSBpZmVsc2UodGVzdCA9IChvcHBvcnR1bml0eSA9PSAiU2FtZSB0aW1lLCBzYW1lIHBsYWNlISIgJiB0YWxrYXRpdmVNb29kID09IFRSVUUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJCbGlzcyIsIG5vID0gIk9oIHdlbGwuLi4iKSkKCnRhbGx5KH4gcmVzdWx0LCBmb3JtYXQgPSAicGVyY2VudCIsIGRhdGEgPSBzaW1fbWVldEV4cCkKCmRlc3RpbnlNb2RlbCA8LSBiaW5vbS50ZXN0KH4gcmVzdWx0LCBuLCBzdWNjZXNzID0gIkJsaXNzIiwgZGF0YSA9IHNpbV9tZWV0RXhwKQpjb25maW50KGRlc3RpbnlNb2RlbCkKYGBgCgpgYGB7cn0Kc2ltX21lZXRFeHAgJT4lCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0gZGVzdGlueSwgeSA9IHlvdSwgY29sb3IgPSByZXN1bHQpLCBhbHBoYSA9IDAuMykgKyAKICB5bGFiKCJZb3UgYXJyaXZlIChtaW51dGVzIGFmdGVyIDEwYW0pIikgKyAKICB4bGFiKCJEZXN0aW55IGFycml2ZXMgKG1pbnV0ZXMgYWZ0ZXIgMTBhbSkiKSArIAogIGdndGl0bGUoIlRoaXMgaXMgd2h5IGl0J3Mgc28gaGFyZCB0byBtZWV0IHBlb3BsZS4uLiIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNjApKSArIAogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYwKSkgCiAgCmBgYAoKPCEtLSBEYXk1OyBtb3ZlZCBhZHZpc2luZyBzaW0gdG8gRGF5NiAtLT4KCgojIyBTaW11bGF0aW5nIE1vZGVscwoKLSBJdCdzIG9mdGVuIGhhbmR5IHRvIGRpcmVjdGx5IHNpbXVsYXRlIGEgc3RhdGlzdGljYWwgbW9kZWwgb2Ygc29tZSBraW5kIGluIG9yZGVyIHRvIGJ1aWxkIGludHVpdGlvbiBhYm91dCBpdCdzIHByb3BlcnRpZXMKICAgIC0gU2ltdWxhdGUgZGF0YSB0aGF0IGNvdWxkIGhhdmUgYmVlbiBwcm9kdWNlZCBieSBzb21lIG1vZGVsCiAgICAtIFNpbXVsYXRlIHNlbnNpdGl2aXR5IHRvIG1vZGVsIGFzc3VtcHRpb25zCgoKCiMjIFNpbXVsYXRpbmcgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGRhdGEKCkNvbnNpZGVyIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsOgoKXFt5ID0gXGJldGFfMCArIFxiZXRhXzEqeFxdCgotIHN1Y2ggdGhhdAogICAgLSBZIGlzIHNvbWUgbWVhc3VyYWJsZSByZXNwb25zZSB2YXJpYWJsZSB0aGF0IHdlIHdhbnQgdG8gcHJlZGljdAogICAgLSBYIGlzIHNvbWUgbWVhc3VyYWJsZSBleHBsYW5hdG9yeSB2YXJpYWJsZSB1c2VkIHRvIHByZWRpY3QgWQotIGFnYWluLCB3ZSBuZWVkIHRvIGludGVncmF0ZSBzb21lIGRlc2lyZWQgbW9kZWwgd2l0aCBzb21lICJub2lzZSIgZHVlIHRvIHJhbmRvbW5lc3MKICAgIC0gV2hhdCB3aWxsIGJlICoqZml4ZWQqKj8KICAgIC0gV2hhdCB3aWxsIGJlICoqcmFuZG9tKio/CgoKIyMgU2ltdWxhdGluZyBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gZGF0YQoKQ29uc2lkZXIgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsOgoKXFtFW1kgfCBYXSA9IFxiZXRhXzAgKyBcYmV0YV8xKlhcXQoKXFt5X2kgPSBiXzAgKyBiXzEqeF9pICsgXGVwc2lsb25faVxdCgotIFE6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHJlcHJlc2VudGF0aW9ucz8KLSBROiBDYW4geW91IHNwb3Qgb3VyIGlucHV0cyBpbiB0aGUgbW9kZWwgc3VtbWFyeT8KLSBROiBXaGF0IGlmIHdlIGNoYW5nZSB0aGUgc2FtcGxlIHNpemU/CgoKYGBge3J9CiMgYmV0YV8wIAppbnRlcmNlcHQgPC0gMjYKCiMgYmV0YV8xCmJldGEgPC0gLTIuNQoKIyBzYW1wbGUgc2l6ZSBmb3Igb3VyIHNpbXVsYXRlZCBtb2RlbApuIDwtIDUwMDAKCiMgWCBpcyBhIHNpbXVsYXRlZCByYW5kb20gdmFyaWFibGUgcmVwcmVzZW50aW5nIHNvbWUgbWVhc3VyYWJsZSBjaGFyYWN0ZXJpc3RpYyBvZiB0aGUgY2FzZXMgCnh0ZXN0IDwtIHJub3JtKG4sIG1lYW4gPSAxMCwgc2QgPSAxKQoKIyBzaW11bGF0ZSBlcnJvcnM6IGFzc3VtZSBpLmkuZC4gTigwLCBzaWdtYSkKZXJyb3JzIDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDUpCgojIFkgaXMgYSBzaW11bGF0ZWQgcmFuZG9tIHZhcmlhYmxlIHJlcHJlc2VudGluZyBvdXIgcmVzcG9uc2UKeXRlc3QgPC0gaW50ZXJjZXB0ICsgKHh0ZXN0ICogYmV0YSkgKyBlcnJvcnMKCiMgZml0IHRoZSBzaW11bGF0ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApzaW1yZWcgPC0gbG0oeXRlc3QgfiB4dGVzdCkKCiMgaG93IGRpZCB3ZSBkbz8KbXN1bW1hcnkoc2ltcmVnKQojIGNvbmZpbnQoc2ltcmVnKQoKYGBgCgoKCgoKIyMgU2ltdWxhdGluZyBsb2dpc3RpYyByZWdyZXNzaW9uIGRhdGEKCkNvbnNpZGVyIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbDoKClxbbG9nKFxmcmFje1xwaX17MS1ccGl9KSA9IFxiZXRhXzAgKyBcYmV0YV8xKnhcXQoKLSBzdWNoIHRoYXQsIAogICAgLSAkXHBpJCByZXByZXNlbnRzIHRoZSBwb3B1bGF0aW9uIHByb2JhYmlsaXR5IG9mICJzdWNjZXNzIiBhY2NvcmRpbmcgdG8gc29tZSBiaW5hcnkgb3V0Y29tZSBvZiBpbnRlcmVzdCB0byB1cwogICAgLSBYIGlzIHNvbWUgbWVhc3VyYWJsZSBleHBsYW5hdG9yeSB2YXJpYWJsZSB1c2VkIHRvIHByZWRpY3QgdGhlIG9kZHMgb2YgInN1Y2Nlc3MiCgotIGFnYWluLCB3ZSBuZWVkIHRvIGludGVncmF0ZSBzb21lIGRlc2lyZWQgbW9kZWwgd2l0aCBzb21lICJub2lzZSIgZHVlIHRvIHJhbmRvbW5lc3MKICAgIC0gV2hhdCB3aWxsIGJlICoqZml4ZWQqKj8KICAgIC0gV2hhdCB3aWxsIGJlICoqcmFuZG9tKio/CgoKIyMgU2ltdWxhdGluZyBsb2dpc3RpYyByZWdyZXNzaW9uIGRhdGEKCkNvbnNpZGVyIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbDoKClxbbG9nKFxmcmFje1xwaX17MS1ccGl9KSA9IFxiZXRhXzAgKyBcYmV0YV8xKnhcXQoKXFtsb2coXGZyYWN7cF9pfXsxLXBfaX0pID0gYl8wICsgYl8xKnhfaVxdCgotIFE6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHJlcHJlc2VudGF0aW9ucz8KLSBROiBXaHkgdGhpcyBzdGVwPyBgaWZlbHNlKHJ1bmlmKG4pIDwgcHJvYiwgMSwgMClgCgoKYGBge3J9CiMgYmV0YV8wCmludGVyY2VwdCA8LSAtMQoKIyBiZXRhXzEKYmV0YSA8LSAwLjUKCiMgc2FtcGxlIHNpemUgZm9yIG91ciBzaW11bGF0ZWQgbW9kZWwKbiA8LSA1MDAwCgojIFggaXMgYSBzaW11bGF0ZWQgcmFuZG9tIHZhcmlhYmxlIHJlcHJlc2VudGluZyBzb21lIG1lYXN1cmFibGUgY2hhcmFjdGVyaXN0aWMgb2YgdGhlIGNhc2VzIAp4dGVzdCA8LSBybm9ybShuLCBtZWFuID0gMSwgc2QgPSAxKQoKIyByZWNhbGw6IHRoZSBsb2ctb2RkcyBpcyBhICpsaW5lYXIqIG1vZGVsIHdpdGggcmVzcGVjdCB0byBvdXIgZXhwbGFuYXRvcnkvcHJlZGljdG9yIHZhcmlhYmxlcwpsaW5wcmVkIDwtIGludGVyY2VwdCArICh4dGVzdCAqIGJldGEpCgojIHRyYW5zZm9ybSBwcmVkaWN0aW9ucyB0byBwcm9iYWJpbGl0aWVzCnByb2IgPC0gZXhwKGxpbnByZWQpLygxICsgZXhwKGxpbnByZWQpKQoKIyBZIGlzIGEgc2ltdWxhdGVkIHJhbmRvbSB2YXJpYWJsZSByZXByZXNlbnRpbmcgb3VyIHJlc3BvbnNlOyB0aGUgImVycm9ycyIgYXJlIGJ1aWx0LWluIGhlcmUKeXRlc3QgPC0gaWZlbHNlKHJ1bmlmKG4pIDwgcHJvYiwgMSwgMCkKCiMgZml0IHRoZSBzaW11bGF0ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApsb2dyZWcgPC0gZ2xtKHl0ZXN0IH4geHRlc3QsIGZhbWlseT1iaW5vbWlhbCkKCiMgaG93IGRpZCB3ZSBkbz8KY29lZihsb2dyZWcpCmNvbmZpbnQobG9ncmVnKQoKIyMgaW50ZXJwcmV0IG9kZHMKZXhwKGNvZWYobG9ncmVnKSkKZXhwKGNvbmZpbnQobG9ncmVnKSkKYGBgCgoKCiMjIEV2YWx1YXRpbmcgTW9kZWwgQXNzdW1wdGlvbnMKCi0gQWxsIG1vZGVscyBoYXZlIGFzc3VtcHRpb25zIG9mIG9uZSBzb3J0IG9yIGFub3RoZXIKLSBXaGVuIGFzc3VtcHRpb25zIGFyZSB2aW9sYXRlZCwgdGhlIGNvbmNsdXNpb25zIG9mIHRoZSBtb2RlbCBtYXkgYmUgamVvcGFyZGl6ZWQKICAgIC0gTm90IGFsbCB2aW9sYXRpb25zIGFyZSBlcXVhbGx5IGRhbmdlcm91cy4uLgogICAgLSBROiAqKkhvdyBjYW4gd2UgdGVsbCB3aGljaCBpcyB3aGljaD8/KioKCgojIyBFdmFsdWF0aW5nIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiBBc3N1bXB0aW9ucwoKLSBROiBXaGF0IGFyZSB0aGUgKDQpIGFzc3VtcHRpb25zIG9mIGxpbmVhciByZWdyZXNzaW9uPwotIFE6IEhvdyBtaWdodCB3ZSB1c2Ugc2ltdWxhdGlvbiB0byAiY2hhbGxlbmdlIiBzb21lIG9mIHRoZXNlIGFzc3VtcHRpb25zIGFuZCBzZWUgaG93IHNlbnNpdGl2ZSB0aGV5IGFyZT8KCgojIyBDaGFsbGVuZ2luZyB0aGUgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbgoKXFtFW1l8WF0gPSBcYmV0YV8wICsgXGJldGFfMVhfMSArIFxiZXRhXzJYXzJcXQoKXFt5X2kgPSBiXzAgKyBiXzF4X3sxaX0gKyBiXzJ4X3syaX0gKyBcZXBzaWxvbl9pXF0KCi0gUTogSG93IG1pZ2h0IHdlICoqY2hhbGxlbmdlKiogdGhlIGVxdWFsIHZhcmlhbmNlIGFzc3VtcHRpb24/Ci0gUTogV2hhdCBhY3R1YWxseSAqKmdvZXMgd3JvbmcqKiB3aGVuIHdlIGhhdmUgYSBwcm9ibGVtIG9mIHVuZXF1YWwgdmFyaWFuY2U/CgpgYGB7cn0KIyBzYW1wbGUgc2l6ZSBmb3Igb3VyIHNpbXVsYXRpb24KbiA8LSAyNTAKCiMgc2Qgb2YgbW9kZWwgZXJyb3JzCnJtc2UgPC0gMQoKIyB0d28gZXhwbGFuYXRvcnkgdmFyaWFibGVzCngxIDwtIHJ1bmlmKG4sIG1pbiA9IDAsIG1heCA9IDE1KQoKIyBtb2RlbCBjb2VmZmljaWVudHMKYmV0YTAgPC0gLTMKYmV0YTEgPC0gMC41CgoKIyBzaW11bGF0ZSByZXNwb25zZSBkYXRhCnkgPC0gYmV0YTAgKyBiZXRhMSp4MSArIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IHJtc2UpICAgICAgICAjIGVxdWFsIHZhcmlhbmNlCiMgeSA8LSBiZXRhMCArIGJldGExKngxICsgcm5vcm0obiwgbWVhbiA9IDAsIHNkID0gcm1zZSArIHgxKSAgICMgVU5lcXVhbCB2YXJpYW5jZQoKIyBwbG90IGRhdGEgd2l0aCBzbW9vdGhlcgpkYXRhLmZyYW1lKHksIHgxKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB4MSwgeSA9IHkpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQoKIyByZWdyZXNzaW9uIG1vZGVsCnNpbUxNIDwtIGxtKHkgfiB4MSkKCgojIGNoZWNrIHRoZSBtb2RlbCBmaXQKbXN1bW1hcnkoc2ltTE0pCm1wbG90KHNpbUxNKVsxXSAgIyByZXNpZHVhbCBkaWFnbm9zdGljcwoKCmBgYAoKCgoKIyMgU2ltdWxhdGUgbWFueSBtb2RlbHMKCi0gUTogV2hhdCBkbyBleHBlY3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwYXJhbWV0ZXIgZXN0aW1hdGVzIHRvIGxvb2sgbGlrZT8KICAgIC0gSWYgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbiBpcyBzYXRpc2ZpZWQKICAgIC0gSWYgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbiBpcyBOT1Qgc2F0aXNmaWVkCgoKYGBge3J9CmRvc2ltIDwtIGZ1bmN0aW9uKCkgewogIHkgPC0gYmV0YTAgKyBiZXRhMSp4MSArIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IHJtc2UgKyB4MSkgICAjIGRlcGFydHVyZQogIG1vZCA8LSBsbSh5IH4geDEpCiAgcmVzdWx0IDwtIGNvZWZmaWNpZW50cyhtb2QpCiAgcmV0dXJuKHJlc3VsdCkKCn0KCnNpbXMgPC0gbW9zYWljOjpkbygxMDAwKSAqIGRvc2ltKCkKZmF2c3RhdHMofiB4MSwgZGF0YSA9IHNpbXMpCgojIGRpc3RyaWJ1dGlvbiBjb2VmZmljaWVudCBlc3RpbWF0ZXMKc2ltcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSB4MSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgIyBwYXNzIGFyZ3VtZW50cyB0byBgZG5vcm1gIGZ1bmN0aW9uCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4oc2ltcyR4MSksIHNkID0gc2Qoc2ltcyR4MSkpLAogICAgICAgICAgICAgICAgbGluZXR5cGUgPSAyKSArCiAgZ2d0aXRsZSgiZGlzdHJpYnV0aW9uIG9mIHJlZ3Jlc3Npb24gcGFyYW1ldGVyIikgKwogIHNjYWxlX3hfY29udGludW91cygiYmV0YSAxIGNvZWZmaWNpZW50cyIpCgpgYGAKCgoKCiMjIFNpbXVsYXRpbmcgYSBjb21wbGV4IHN5c3RlbQoKCmBgYHtyfQphbnlfYWN0aXZlIDwtIGZ1bmN0aW9uKGRmKSB7CiAgIyByZXR1cm4gVFJVRSBpZiBzb21lb25lIGhhcyBub3QgZmluaXNoZWQKICByZXR1cm4obWF4KGRmJGVuZHRpbWUpID09IEluZikKfQoKbmV4dF9jdXN0b21lciA8LSBmdW5jdGlvbihkZikgewogICMgcmV0dXJucyB0aGUgbmV4dCBjdXN0b21lciBpbiBsaW5lCiAgcmVzIDwtIGZpbHRlcihkZiwgZW5kdGltZSA9PSBJbmYpICU+JQogICAgYXJyYW5nZShhcnJpdmFsKQogIHJldHVybihoZWFkKHJlcywgMSkpCn0KCnVwZGF0ZV9jdXN0b21lciA8LSBmdW5jdGlvbihkZiwgY3VzdF9udW0sIGVuZF90aW1lKSB7CiAgIyBzZXRzIHRoZSBlbmQgdGltZSBvZiBhIHNwZWNpZmljIGN1c3RvbWVyCiAgcmV0dXJuKG11dGF0ZShkZiwgZW5kdGltZSA9IGlmZWxzZShjdXN0bnVtID09IGN1c3RfbnVtLCBlbmRfdGltZSwgZW5kdGltZSkpKQp9CiAgCmBgYAoKCmBgYHtyfQpydW5fc2ltIDwtIGZ1bmN0aW9uKG4gPSAxLzIsIG0gPSAzLzIsIGhvdXJzID0gNikgewogICMgc2ltdWxhdGlvbiBvZiBiYW5rIHdoZXJlIHRoZXJlIGlzIGp1c3Qgb25lIHRlbGxlcgogICMgbjogZXhwZWN0ZWQgbnVtYmVyIG9mIGN1c3RvbWVycyBwZXIgbWludXRlCiAgIyBtOiBleHBlY3RlZCBsZW5ndGggb2YgdHJhbnNhY3Rpb24gaXMgbSBtaW51dGVzCiAgIyBob3VyczogYmFuayBvcGVuIGZvciB0aGlzIG1hbnkgaG91cnMKICAKICBjdXN0b21lcnMgPC0gcnBvaXMoaG91cnMgKiA2MCwgbGFtYmRhID0gbikKICBhcnJpdmFsIDwtIG51bWVyaWMoc3VtKGN1c3RvbWVycykpCiAgcG9zaXRpb24gPC0gMQogIGZvciAoaSBpbiAxOmxlbmd0aChjdXN0b21lcnMpKSB7CiAgICBudW1jdXN0IDwtIGN1c3RvbWVyc1tpXQogICAgaWYgKG51bWN1c3QgIT0gMCkgewogICAgICBhcnJpdmFsW3Bvc2l0aW9uOihwb3NpdGlvbiArIG51bWN1c3QgLSAxKV0gPC0gcmVwKGksIG51bWN1c3QpCiAgICAgIHBvc2l0aW9uIDwtIHBvc2l0aW9uICsgbnVtY3VzdAogICAgfQogIH0KICBkdXJhdGlvbiA8LSByZXhwKGxlbmd0aChhcnJpdmFsKSwgcmF0ZSA9IDEvbSkgICMgRVtYXT1tCiAgZGYgPC0gZGF0YS5mcmFtZShhcnJpdmFsLCBkdXJhdGlvbiwgY3VzdG51bSA9IDE6bGVuZ3RoKGR1cmF0aW9uKSwgCiAgICAgICAgICAgICAgICAgICBlbmR0aW1lID0gSW5mLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgCiAgZW5kdGltZSA8LSAwICMgc2V0IHVwIGJlZ2lubmluZyBvZiBzaW11bGF0aW9uCiAgd2hpbGUgKGFueV9hY3RpdmUoZGYpKSB7ICMgYW55b25lIGxlZnQgdG8gc2VydmUKICAgIG5leHRfb25lIDwtIG5leHRfY3VzdG9tZXIoZGYpCiAgICBub3cgPC0gaWZlbHNlKG5leHRfb25lJGFycml2YWwgPj0gZW5kdGltZSwgbmV4dF9vbmUkYXJyaXZhbCwgZW5kdGltZSkKICAgIGVuZHRpbWUgPC0gbm93ICsgbmV4dF9vbmUkZHVyYXRpb24KICAgIGRmIDwtIHVwZGF0ZV9jdXN0b21lcihkZiwgbmV4dF9vbmUkY3VzdG51bSwgZW5kdGltZSkKICB9CiAgZGYgPC0gbXV0YXRlKGRmLCB0b3RhbHRpbWUgPSBlbmR0aW1lIC0gYXJyaXZhbCkKICByZXR1cm4oZmF2c3RhdHMofiB0b3RhbHRpbWUsIGRhdGEgPSBkZikpCn0KCmBgYAoKCmBgYHtyfQpzaW1fcmVzdWx0cyA8LSBtb3NhaWM6OmRvKDMpICogcnVuX3NpbSgpCnNpbV9yZXN1bHRzCmBgYAoKCgo=